package toman;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: zslajchrt
 * Date: 2.5.11
 * Time: 19:03
 * To change this template use File | Settings | File Templates.
 */
public class DefaultPropertyEvaluator implements PropertyEvaluator {

    public static final DefaultPropertyEvaluator INSTANCE = new DefaultPropertyEvaluator();

    public static Object getPropertyValueByRefl(String propertyName, Object obj, PropertyInspector propertyInspector) {
        String propGetter = "get" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
        try {
            Method getter = obj.getClass().getMethod(propGetter);
            if (propertyInspector != null) {
                propertyInspector.inspect(getter);
            }
            return getter.invoke(obj);
        } catch (NoSuchMethodException e) {

            return getPropertyValueFromFieldByRefl(propertyName, obj, propertyInspector, obj.getClass());

        } catch (InvocationTargetException e) {
            throw new IllegalArgumentException("Accessing the method for property " + propertyName, e);
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException("Accessing the method for property " + propertyName, e);
        }
    }

    public static Object getPropertyValueFromFieldByRefl(String propertyName, Object obj, PropertyInspector propertyInspector,
                                                          Class cls) {
        try {
            Field propField = cls.getDeclaredField(propertyName);
            if (propertyInspector != null) {
                propertyInspector.inspect(propField);
            }
            propField.setAccessible(true);
            return propField.get(obj);
        } catch (NoSuchFieldException e1) {

            if (cls.getSuperclass() == Object.class)
                throw new IllegalArgumentException("Neither a getter nor a field found for property " + propertyName, e1);
            else
                return getPropertyValueFromFieldByRefl(propertyName, obj, propertyInspector, cls.getSuperclass());

        } catch (IllegalAccessException e1) {
            throw new IllegalArgumentException("Accessing the field for property " + propertyName, e1);
        }
    }

    public static <A extends Annotation> A getMemberAnnotation(Class<A> annotClass, Object property) {
        if (property instanceof Field) {
            return ((Field) property).getAnnotation(annotClass);
        } else if (property instanceof Method) {
            return ((Method) property).getAnnotation(annotClass);
        } else {
            return null;
        }
    }

    public Object getPropertyValue(Object contextObject, String propertyName) {
        if (contextObject instanceof Map) {
            return ((Map) contextObject).get(propertyName);
        }
        return getPropertyValueByRefl(propertyName, contextObject, null);
    }

    public <A extends Annotation> A getPropertyAnnotation(Object contextObject, final Class<A> annotClass, String propertyName) {
        if (contextObject instanceof Map) {
            return null;
        }
        final Object[] annotSlot = new Object[1];
        getPropertyValueByRefl(propertyName, contextObject, new PropertyInspector() {
            public void inspect(Object property) {
                annotSlot[0] = getMemberAnnotation(annotClass, property);
            }
        });

        return (A) annotSlot[0];
    }
}
